﻿// UDPPunchServer.cpp: 定义应用程序的入口点。
//

#include "UDPPunchServer.h"

USHORT ServPort = SERVER_PORT;  //服务器端口
UserList clntList;              //客户端链表

void initWinSock()
{
	WSADATA wsData;
	if (WSAStartup(MAKEWORD(2, 2), &wsData) != 0)
	{
		SockErrorHandle("Windows socket 2.2 startup error.");
	}
	std::cout << "Using " << wsData.szDescription << " ( Status: " << wsData.szSystemStatus << " )" << std::endl;
	printf("With API Version %d.%d to %d.%d\n", LOBYTE(wsData.wVersion), LOBYTE(wsData.wVersion), LOBYTE(wsData.wHighVersion), LOBYTE(wsData.wHighVersion));
}

SOCKET create_socket(int af, int type, int protocol)
{
	SOCKET sock = socket(af, type, protocol);
	if (sock == INVALID_SOCKET)
	{
		SockErrorHandle("create socket error.");
	}
	return sock;
}

cltUserNode getClient(const char* username)
{
	UserList::iterator userIter = clntList.begin();
	for (; userIter != clntList.end(); ++userIter)
	{
		if (strcmp((*userIter)->username, username) == 0)
		{
			return *(*userIter);
		}
	}
	return *(*userIter);
}


int main(int argc, char* argv[]) {
	if (argc > 1) {
		ServPort = atoi(argv[1]);
	}
	if (argc > 2) {
		std::cout << "Usage: " << argv[0] << " [PORT]" << std::endl;
		return 1;
	}

	try {
		//1.初始化winsock版本库
		initWinSock();

		//2.创建UDP套接字
		SOCKET serv_sock = create_socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);

		//3.为UDP套接字分配地址信息(IP和PORT)
		SOCKADDR_IN serv_addr;
		serv_addr.sin_family = AF_INET;
		serv_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
		serv_addr.sin_port = htons(ServPort);
		if (bind(serv_sock, (SOCKADDR*)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR)
		{
			SockErrorHandle("socket bind IP and PORT error.");
		}

		//开始主循环
		//主循环主要负责下面几件事情：
		//  1.读取客户端登录和登出信息，记录客户列表
		//  2.转发客户端P2P请求
		int str_len;
		int clnt_addr_size;
		SOCKADDR_IN clnt_addr;  //客户端地址信息
		cltMessage recvBuff;    //服务端接收数据缓冲
		memset(&recvBuff, 0, sizeof(cltMessage));
		int session_id = 1;
		while (true)
		{
			std::cout << "-----SESSION " << session_id++ << " STARTUP-----" << std::endl;

			clnt_addr_size = sizeof(clnt_addr);
			//接收客户端数据
			str_len = recvfrom(serv_sock, (char*)&recvBuff, sizeof(cltMessage), 0, (SOCKADDR*)&clnt_addr, &clnt_addr_size);
			if (str_len <= 0)
			{
				std::cout << "receive client's data error." << std::endl;
				continue;
			}

			//处理数据
			int msgType = recvBuff.iMessageType;
			switch (msgType)
			{
				//登录
				case LOGIN:
				{
					//将这个用户记录到用户节点中
					cltUserNode* currClientUser = new cltUserNode();
					strcpy_s(currClientUser->username, 10, recvBuff.message.cltLoginMsg.username);
					currClientUser->ip = ntohl(clnt_addr.sin_addr.S_un.S_addr);
					currClientUser->port = ntohs(clnt_addr.sin_port);

					//查找用户节点列表
					bool bFound = false;
					for (UserList::iterator usrIter = clntList.begin(); usrIter != clntList.end(); ++usrIter)
					{
						if (strcmp((*usrIter)->username, recvBuff.message.cltLoginMsg.username) == 0)
						{
							bFound = true;
							break;
						}
					}

					//将登录用户加入到用户列表中
					if (!bFound)
					{
						std::cout << "has a client user login: " << recvBuff.message.cltLoginMsg.username;
						std::cout << " < " << inet_ntoa(clnt_addr.sin_addr);
						std::cout << " : " << ntohs(clnt_addr.sin_port) << " >" << std::endl;

						clntList.push_back(currClientUser);
					}

					//返回登录用户信息给客户端
					int nodecount = (int)clntList.size();
					sendto(serv_sock, (const char*)&nodecount, sizeof(int), 0, (SOCKADDR*)&clnt_addr, sizeof(clnt_addr));
					for (UserList::iterator usrIter = clntList.begin(); usrIter != clntList.end(); ++usrIter)
					{
						sendto(serv_sock, (const char*)(*usrIter), sizeof(cltUserNode), 0, (SOCKADDR*)&clnt_addr, sizeof(clnt_addr));
					}

					std::cout << "send user list info to: " << recvBuff.message.cltLoginMsg.username;
					std::cout << " < " << inet_ntoa(clnt_addr.sin_addr);
					std::cout << " : " << ntohs(clnt_addr.sin_port) << " >" << std::endl;
					break;
				}

				//登出
				case LOGOUT:
				{
					//将客户端信息从用户列表中删除
					UserList::iterator removIter = clntList.begin();
					for (UserList::iterator usrIter = clntList.begin(); usrIter != clntList.end(); ++usrIter)
					{
						if (strcmp((*usrIter)->username, recvBuff.message.cltLogoutMsg.username) == 0)
						{
							removIter = usrIter;
							break;
						}
					}
					if (removIter != clntList.end())
					{
						clntList.remove(*removIter);
					}

					std::cout << "has a client user logout: " << recvBuff.message.cltLogoutMsg.username;
					std::cout << " < " << inet_ntoa(clnt_addr.sin_addr);
					std::cout << " : " << ntohs(clnt_addr.sin_port) << " >" << std::endl;
					break;
				}

				//转发客户端请求
				case P2PTRANS:
				{
					//某个客户端希望服务器转发给另一个客户端一个UDP打洞的请求消息
					std::cout << "< " << inet_ntoa(clnt_addr.sin_addr);
					std::cout << " : " << ntohs(clnt_addr.sin_port);
					std::cout << " > want to P2P client: " << recvBuff.message.cltTransMsg.username << std::endl;

					cltUserNode remoteNode = getClient(recvBuff.message.cltTransMsg.username);
					SOCKADDR_IN remote_addr;
					remote_addr.sin_family = AF_INET;
					remote_addr.sin_addr.S_un.S_addr = htonl(remoteNode.ip);
					remote_addr.sin_port = htons(remoteNode.port);

					in_addr tmp;
					tmp.S_un.S_addr = htonl(remoteNode.ip);

					cltP2PMessage transMsg;
					transMsg.iMessageType = P2PSOMEONEWANTTOCALLYOU;
					transMsg.iStringLen = ntohl(clnt_addr.sin_addr.S_un.S_addr);
					transMsg.Port = ntohs(clnt_addr.sin_port);

					//转发P2P请求信息
					sendto(serv_sock, (const char*)&transMsg, sizeof(transMsg), 0, (SOCKADDR*)&remote_addr, sizeof(remote_addr));

					std::cout << "tell " << recvBuff.message.cltTransMsg.username;
					std::cout << ": < " << inet_ntoa(remote_addr.sin_addr);
					std::cout << " : " << ntohs(remote_addr.sin_port);
					std::cout << " > should send P2P message to < " << inet_ntoa(clnt_addr.sin_addr);
					std::cout << " : " << ntohs(clnt_addr.sin_port) << " >" << std::endl;
					break;
				}

				//获取所有客户端用户
				case GETALLUSER:
				{
					int command = GETALLUSER;
					sendto(serv_sock, (const char*)&command, sizeof(int), 0, (SOCKADDR*)&clnt_addr, sizeof(clnt_addr));

					int nodecount = (int)clntList.size();
					sendto(serv_sock, (const char*)&nodecount, sizeof(int), 0, (SOCKADDR*)&clnt_addr, sizeof(clnt_addr));

					for (UserList::iterator usrIter = clntList.begin(); usrIter != clntList.end(); ++usrIter) {
						//发送客户端用户信息
						sendto(serv_sock, (const char*)(*usrIter), sizeof(cltUserNode), 0, (SOCKADDR*)&clnt_addr, sizeof(clnt_addr));
					}

					std::cout << "send client user list info to: " << recvBuff.message.cltLoginMsg.username;
					std::cout << " < " << inet_ntoa(clnt_addr.sin_addr);
					std::cout << " : " << ntohs(clnt_addr.sin_port) << " > " << std::endl;
					break;
				}
			}
		}
	}
	catch (std::exception& e) {
		std::cout << "something is error : " << e.what() << std::endl;
		return 1;
	}
}